﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Common.Extensions;
using Windows.UI.Xaml.Shapes;
using Windows.UI;
using System.Windows.Input;
using Common.UI.Controls.Utils;
using Common.Utils;

// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236

namespace Common.UI.Controls
{
	public sealed partial class BarChart : UserControl
	{
		private Double MIN_BAR_WIDTH = 40;
		private List<MapItem> MAP_ITEMS = new List<MapItem>() 
		{
 			MapItem.Create(2.5, 3),
			MapItem.Create(5, 3),
			MapItem.Create(10, 4),
			MapItem.Create(15, 3),
			MapItem.Create(20, 4),
			MapItem.Create(25, 5),
			MapItem.Create(30, 3),
			MapItem.Create(40, 4),
			MapItem.Create(50, 5),
			MapItem.Create(60, 3),
			MapItem.Create(100, 4),
			MapItem.Create(125, 5),
			MapItem.Create(150, 3),
			MapItem.Create(200, 4),
			MapItem.Create(250, 5),
			MapItem.Create(300, 6),
			MapItem.Create(10000, 5),
		};

		private Double _lastUpdatedHeight;
		private Double _lastUpdatedWidth;
		private Guid _lastUpdatedSourceId;

		public BarChart()
		{
			this.InitializeComponent();
		}


		public DoubleBarChartItemsSource Values
		{
			get { return (DoubleBarChartItemsSource)GetValue(ValuesProperty); }
			set { SetValue(ValuesProperty, value); }
		}

		public static readonly DependencyProperty ValuesProperty =
			DependencyProperty.Register("Values", typeof(DoubleBarChartItemsSource), typeof(BarChart), new PropertyMetadata(null, OnValuesChanged));

		private static void OnValuesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			(d as BarChart).Update();
		}


		public GuidRelayCommand Clicked
		{
			get { return (GuidRelayCommand)GetValue(ClickedProperty); }
			set { SetValue(ClickedProperty, value); }
		}

		public static readonly DependencyProperty ClickedProperty =
			DependencyProperty.Register("Clicked", typeof(GuidRelayCommand), typeof(BarChart), new PropertyMetadata(null));




		private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			Update(ActualWidth, ActualHeight);
		}

		private void Update()
		{
			Update(ActualWidth, ActualHeight);
		}

		private void Update(double canvasWidth, double canvasHeight)
		{
			if (canvasWidth == 0 && canvasHeight == 0)
				return;


			var valuesSource = Values as DoubleBarChartItemsSource;
			if (valuesSource == null)
				return;

			if (valuesSource.Id == _lastUpdatedSourceId && canvasHeight == _lastUpdatedHeight && canvasWidth == _lastUpdatedWidth)
				return;

			Update(Values as DoubleBarChartItemsSource, canvasWidth, canvasHeight);

			_lastUpdatedSourceId = valuesSource.Id;
			_lastUpdatedHeight = canvasHeight;
			_lastUpdatedWidth = canvasWidth;
		}

		private void Update(DoubleBarChartItemsSource valuesSource, double canvasWidth, double canvasHeight)
		{
			Bars.Children.Clear();

			if (valuesSource.Count == 0)
				return;

			UpdateLL(MapValue(valuesSource.Max), GetStepsCount(valuesSource.Max));

			foreach (var bar in RelativeBars(valuesSource, canvasWidth, canvasHeight, this.Background, this.Foreground).ToList())
			{
				Bars.Children.Add(bar);
			}
		}

		private int GetStepsCount(double v)
		{
			return MAP_ITEMS.First(x => v <= x.Value).StepsCount;
		}

		private IEnumerable<UIElement> RelativeBars(DoubleBarChartItemsSource valuesSource, double canvasWidth, double canvasHeight, Brush backColor, Brush topColor, double barRate = 0.5)
		{
			var maxWidth = canvasWidth / valuesSource.Count;
			var tmp = Math.Min(maxWidth * barRate, MIN_BAR_WIDTH * 2.2);
			var left = (maxWidth - tmp) * 0.5;
			var barWidth = tmp * 0.4;
			var middle = tmp * 0.2;

			return valuesSource.ForEach<UIElement>(
				(res, frontItem, backItem, i, isLast) =>
				{
					var r1 = new Rectangle()
					{
						Height = backItem.Value / MapValue(valuesSource.Max) * canvasHeight,
						Width = barWidth,
						Margin = new Thickness(left, 0, isLast ? 0.0 : middle, 0),
						VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Bottom,
						Fill = backColor,
						StrokeThickness = 0.0,
						RadiusX = 2,
						RadiusY = 2,
					};

					r1.Tag = backItem.Id;
					r1.Tapped += OnBarTapped;
					r1.RightTapped += OnBarRightTapped;
					res.Add(r1);

					var r2 = new Rectangle()
					{
						Height = frontItem.Value / MapValue(valuesSource.Max) * canvasHeight,
						Width = barWidth,
						Margin = new Thickness(0, 0, isLast ? 0.0 : left, 0),
						VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Bottom,
						Fill = topColor,
						StrokeThickness = 0.0,
						RadiusX = 2,
						RadiusY = 2,
					};

					r2.Tag = frontItem.Id;
					r2.Tapped += OnBarTapped;
					r2.RightTapped += OnBarRightTapped;
					res.Add(r2);
				}
				);
		}

		void OnBarRightTapped(object sender, RightTappedRoutedEventArgs e)
		{
			var id = (Guid)(sender as Rectangle).Tag;
			InvokeClicked(id);
		}

		private void InvokeClicked(Guid id)
		{
			if (Clicked != null)
				Clicked.Execute(id);
		}

		void OnBarTapped(object sender, TappedRoutedEventArgs e)
		{
			var id = (Guid)(sender as Rectangle).Tag;
			InvokeClicked(id);
		}

		private double MapValue(double v)
		{
			return MAP_ITEMS.First(x => v <= x.Value).Value;
		}

		private void UpdateLL(double maxValue, Int32 count)
		{
			BackGrid.RowDefinitions.Clear();
			BackGrid.Children.Clear();

			var step = maxValue / count;


			for (int i = 0; i < count; i++)
			{
				var def = new RowDefinition()
				{
					Height = new GridLength(1, GridUnitType.Star),
				};
				BackGrid.RowDefinitions.Add(def);

				var rect = new Rectangle()
				{
					Height = 1.0,
					StrokeThickness = 0.0,
					VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Top,
					Fill = this.Background,
				};
				Grid.SetRow(rect, i);

				var tb = new TextBlock()
				{
					Text = String.Format("{0:N0}", (count - i) * step),
					FontSize = this.FontSize,
					Margin = new Thickness(0),
					HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Left,
					VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Top,
					Foreground = this.Foreground
				};
				Grid.SetRow(tb, i);

				BackGrid.Children.Add(rect);
				BackGrid.Children.Add(tb);
			}

			var zeroRect = new Rectangle()
			{
				Height = 1.0,
				StrokeThickness = 0.0,
				VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Bottom,
				Fill = new SolidColorBrush(Color.FromArgb(0x7F, 0xFF, 0xFF, 0xFF)),
			};
			Grid.SetRow(zeroRect, count - 1);
			BackGrid.Children.Add(zeroRect);
		}


	}


	public class MapItem
	{
		public MapItem(Double value, Int32 stepsCount)
		{
			Value = value;
			StepsCount = stepsCount;
		}
		public Double Value { get; set; }
		public Int32 StepsCount { get; set; }

		public static MapItem Create(Double value, Int32 stepsCount)
		{
			return new MapItem(value, stepsCount);
		}
	}

	
}
